package com.gitee.luosl.cola.match.cases;

import com.gitee.luosl.cola.lang.Tuple2;
import com.gitee.luosl.cola.lang.Tuple3;
import com.gitee.luosl.cola.lang.Tuple4;
import com.gitee.luosl.cola.lang.function.Consumer3;
import com.gitee.luosl.cola.lang.function.Consumer4;
import com.gitee.luosl.cola.lang.function.Function3;
import com.gitee.luosl.cola.lang.function.Function4;
import com.gitee.luosl.cola.match.guard.Guard;
import com.gitee.luosl.cola.match.pattern.Pattern;
import com.gitee.luosl.cola.match.pattern.Pattern2;
import com.gitee.luosl.cola.match.pattern.Pattern3;
import com.gitee.luosl.cola.match.pattern.Pattern4;

import java.util.function.*;

public class Case<T, U, R> extends AbstractCase <T, U, R> {

    private final Function<? super U, ? extends R> mapper;

    public Case(Pattern<? super T, ? extends U> pattern, Guard<U> guard, Function<? super U, ? extends R> mapper) {
        super(pattern, guard);
        this.mapper = mapper;
    }

    public Case(Pattern<? super T, ? extends U> pattern, Function<? super U, ? extends R> mapper) {
       this(pattern, null, mapper);
    }

    @Override
    public R apply(U value) {
        return mapper.apply(value);
    }


    public static <T, U, R> Case<T, U, R> of(Pattern<T, U> pattern, Function<U, R> function){
        return new Case<>(pattern, function);
    }

    public static <T, U, R> Case<T, U, R> of(Pattern<T, U> pattern, Guard<U> guard, Function<U, R> function){
        return new Case<>(pattern, guard, function);
    }

    public static <T, U, R> Case<T, U, R> of(Pattern<T, U> pattern, Supplier<R> supplier){
        return new Case<>(pattern, ignore -> supplier.get());
    }

    public static <T, U, R> Case<T, U, R> of(Pattern<T, U> pattern, Guard<U> guard, Supplier<R> supplier){
        return new Case<>(pattern, guard, ignore -> supplier.get());
    }

    public static <T, U, R> Case<T, U, R> of(Pattern<T, U> pattern, R value){
        return new Case<>(pattern, ignore -> value);
    }

    public static <T, U, R> Case<T, U, R> of(Pattern<T, U> pattern, Guard<U> guard, R value){
        return new Case<>(pattern, guard, ignore -> value);
    }

    public static <T, T1, T2, R> Case<T, Tuple2<T1, T2>, R> of(Pattern2<T, T1, T2> pattern, BiFunction<? super T1, ? super T2, ? extends R> function){
        return new Case<>(pattern, t -> t.apply(function));
    }

    public static <T, T1, T2, R> Case<T, Tuple2<T1, T2>, R> of(Pattern2<T, T1, T2> pattern, Guard<Tuple2<T1, T2>> guard, BiFunction<T1, T2, R> function){
        return new Case<>(pattern, guard, t -> t.apply(function));
    }

    public static <T, T1, T2, T3, R> Case<T, Tuple3<T1, T2, T3>, R> of(Pattern3<T, T1, T2, T3> pattern, Function3<T1, T2, T3, R> function){
        return new Case<>(pattern, t -> t.apply(function));
    }

    public static <T, T1, T2, T3, R> Case<T, Tuple3<T1, T2, T3>, R> of(Pattern3<T, T1, T2, T3> pattern, Guard<Tuple3<T1, T2, T3>> guard, Function3<T1, T2, T3, R> function){
        return new Case<>(pattern, guard, t -> t.apply(function));
    }

    public static <T, T1, T2, T3, T4, R> Case<T, Tuple4<T1, T2, T3, T4>, R> of(Pattern4<T, T1, T2, T3, T4> pattern, Function4<T1, T2, T3, T4, R> function){
        return new Case<>(pattern, t -> t.apply(function));
    }

    public static <T, T1, T2, T3, T4, R> Case<T, Tuple4<T1, T2, T3, T4>, R> of(Pattern4<T, T1, T2, T3, T4> pattern, Guard<Tuple4<T1, T2, T3, T4>> guard, Function4<T1, T2, T3, T4, R> function){
        return new Case<>(pattern, guard, t -> t.apply(function));
    }

    public static <T, U> Case<T, U, Void> run(Pattern<T, U> pattern, Runnable runnable){
        return of(pattern, ignore -> {runnable.run(); return null;});
    }

    public static <T, U> Case<T, U, Void> run(Pattern<T, U> pattern, Guard<U> guard, Runnable runnable){
        return of(pattern, guard, ignore -> {runnable.run(); return null;});
    }

    public static <T, U> Case<T, U, Void> run(Pattern<T, U> pattern, Consumer<U> consumer){
        return of(pattern, data -> {consumer.accept(data); return null;});
    }

    public static <T, U> Case<T, U, Void> run(Pattern<T, U> pattern, Guard<U> guard, Consumer<U> consumer){
        return of(pattern, guard, data -> {consumer.accept(data); return null;});
    }

    public static <T, T1, T2> Case<T, Tuple2<T1, T2>, Void> run(Pattern2<T, T1, T2> pattern, BiConsumer<T1, T2> consumer){
        return new Case<>(pattern, t -> {consumer.accept(t._1, t._2); return null;});
    }

    public static <T, T1, T2> Case<T, Tuple2<T1, T2>, Void> run(Pattern2<T, T1, T2> pattern, Guard<Tuple2<T1, T2>> guard, BiConsumer<T1, T2> consumer){
        return new Case<>(pattern, guard, t -> {consumer.accept(t._1, t._2); return null;});
    }

    public static <T, T1, T2, T3> Case<T, Tuple3<T1, T2, T3>, Void> run(Pattern3<T, T1, T2, T3> pattern, Consumer3<T1, T2, T3> consumer){
        return new Case<>(pattern, t -> {consumer.accept(t._1, t._2, t._3); return null;});
    }

    public static <T, T1, T2, T3> Case<T, Tuple3<T1, T2, T3>, Void> run(Pattern3<T, T1, T2, T3> pattern, Guard<Tuple3<T1, T2, T3>> guard, Consumer3<T1, T2, T3> consumer){
        return new Case<>(pattern, guard, t -> {consumer.accept(t._1, t._2, t._3); return null;});
    }

    public static <T, T1, T2, T3, T4> Case<T, Tuple4<T1, T2, T3, T4>, Void> run(Pattern4<T, T1, T2, T3, T4> pattern, Consumer4<T1, T2, T3, T4> consumer){
        return new Case<>(pattern, t -> {consumer.accept(t._1, t._2, t._3, t._4); return null;});
    }

    public static <T, T1, T2, T3, T4> Case<T, Tuple4<T1, T2, T3, T4>, Void> run(Pattern4<T, T1, T2, T3, T4> pattern, Guard<Tuple4<T1, T2, T3, T4>> guard, Consumer4<T1, T2, T3, T4> consumer){
        return new Case<>(pattern, guard, t -> {consumer.accept(t._1, t._2, t._3, t._4); return null;});
    }


}
